package com.xlh.support.experimentshare.service.impl;

import com.google.common.collect.*;
import com.xlh.dto.user.UserInfoDTO;
import com.xlh.exception.common.GlobalException;
import com.xlh.service.course.ChapterService;
import com.xlh.support.experimentshare.domain.ShareUser;
import com.xlh.support.experimentshare.service.ExperimentShareConfigService;
import com.xlh.support.experimentshare.service.ExperimentShareService;
import com.xlh.support.experimentshare.vo.*;
import com.xlh.support.experimentshare.websocket.CloseShareWebsocket;
import com.xlh.support.experimentshare.websocket.message.*;
import com.xlh.virtualization.domain.ExperimentContainer;
import com.xlh.websocket.GlobalWebsocket;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static com.xlh.virtualization.constant.ContainerRoleEnum.SLAVE;

/**
 * @author cheer
 */
@Service
@Slf4j
public class ExperimentShareServiceImpl implements ExperimentShareService {

    @Autowired
    private ChapterService chapterService;

    @Autowired
    private ExperimentShareConfigService experimentShareConfigService;

    /**
     * 分享表 分享用户->被分享用户Id->是否允许控制
     */
    private Table<ShareUser, Long, Boolean> shareTable = Tables.synchronizedTable(HashBasedTable.create());

    /**
     * 实验表 分享用户->实验信息
     */
    private Map<ShareUser, ShareExperimentVO> shareExperimentMap = Maps.newConcurrentMap();

    @Override
    public List<ExperimentUserVO> listExperimentUsers(Long userId, Boolean isTeacher, Long chapterId) {
        List<ExperimentUserVO> experimentUserVOS = new ArrayList<>();
        if (isTeacher) {
            List<UserInfoDTO> teachers = chapterService.getChapterTeacher(chapterId);
            if (CollectionUtils.isNotEmpty(teachers)) {
                teachers.stream()
                        .filter(userInfoDTO -> !Objects.equals(userId, userInfoDTO.getId()))
                        .forEach(userInfoDTO -> {
                            ExperimentUserVO experimentUserVO = ExperimentUserVO.builder()
                                    .userId(userInfoDTO.getId())
                                    .username(userInfoDTO.getName() + "（教师）")
                                    .build();
                            experimentUserVOS.add(experimentUserVO);
                        });
            }

            List<UserInfoDTO> students = chapterService.listStudent(chapterId);
            if (CollectionUtils.isNotEmpty(students)) {
                students.forEach(userInfoDTO -> {
                    ExperimentUserVO experimentUserVO = ExperimentUserVO.builder()
                            .userId(userInfoDTO.getId())
                            .username(userInfoDTO.getName())
                            .build();
                    experimentUserVOS.add(experimentUserVO);
                });
            }

        } else if (chapterService.isDefaultStudent(chapterId, userId)) {
            ShareConfigVO shareConfigVO = experimentShareConfigService.getShareConfig(chapterId);
            if (shareConfigVO.getStuToTeaSwitch()) {
                List<UserInfoDTO> teachers = chapterService.getChapterTeacher(chapterId);
                if (CollectionUtils.isNotEmpty(teachers)) {
                    teachers.forEach(userInfoDTO -> {
                        ExperimentUserVO experimentUserVO = ExperimentUserVO.builder()
                                .userId(userInfoDTO.getId())
                                .username(userInfoDTO.getName() + "（教师）")
                                .build();
                        experimentUserVOS.add(experimentUserVO);
                    });
                }
            }
            if (shareConfigVO.getStuToStuSwitch()) {
                List<UserInfoDTO> students = chapterService.listStudent(chapterId);
                if (CollectionUtils.isNotEmpty(students)) {
                    students.stream()
                            .filter(userInfoDTO -> !Objects.equals(userId, userInfoDTO.getId()))
                            .forEach(userInfoDTO -> {
                                ExperimentUserVO experimentUserVO = ExperimentUserVO.builder()
                                        .userId(userInfoDTO.getId())
                                        .username(userInfoDTO.getName())
                                        .build();
                                experimentUserVOS.add(experimentUserVO);
                            });
                }
            }
        }
        return experimentUserVOS;
    }

    @Override
    public void shareExperiment(ShareUser shareUser, List<Long> sharedUserIds, Boolean controllable) {
        // 存储分享表
        for (Long sharedUserId : sharedUserIds) {
            shareTable.put(shareUser, sharedUserId, controllable);
        }

        // 推送分享消息
        ShareMessage shareMessage = ShareMessage.builder()
                .userId(shareUser.getUserId())
                .chapterId(shareUser.getChapterId())
                .username(shareUser.getUsername())
                .build();
        GlobalWebsocket.sendMessage(sharedUserIds, shareMessage);
    }

    @Override
    public void closeShareExperiment(ShareUser shareUser) {
        Map<Long, Boolean> sharedUserMap = shareTable.row(shareUser);
        if (MapUtils.isNotEmpty(sharedUserMap)) {
            List<Long> sharedUserIds = new ArrayList<>(sharedUserMap.keySet());
            // 删除分享表
            for (Long sharedUserId : sharedUserIds) {
                shareTable.remove(shareUser, sharedUserId);
            }

            // 推送关闭分享消息
            ShareCloseMessage shareCloseMessage = ShareCloseMessage.builder()
                    .userId(shareUser.getUserId())
                    .chapterId(shareUser.getChapterId())
                    .username(shareUser.getUsername())
                    .build();
            GlobalWebsocket.sendMessage(sharedUserIds, shareCloseMessage);
            CloseShareWebsocket.sendMessage(shareUser, shareCloseMessage);
        }
    }

    @Override
    public void closeShareExperiment(List<ShareUser> shareUsers) {
        if (CollectionUtils.isNotEmpty(shareUsers)) {
            for (ShareUser shareUser : shareUsers) {
                closeShareExperiment(shareUser);
            }
        }
    }

    @Override
    public List<ShareUserVO> queryShareExperiment(Long userId) {
        List<ShareUserVO> shareUserVOS = new ArrayList<>();
        Map<ShareUser, Boolean> shareMap = shareTable.column(userId);
        if (MapUtils.isNotEmpty(shareMap)) {
            List<ShareUser> shareUsers = new ArrayList<>(shareMap.keySet());
            for (ShareUser shareUser : shareUsers) {
                ShareUserVO shareUserVO = ShareUserVO.builder()
                        .userId(shareUser.getUserId())
                        .chapterId(shareUser.getChapterId())
                        .username(shareUser.getUsername())
                        .build();
                shareUserVOS.add(shareUserVO);
            }
        }
        return shareUserVOS;
    }

    @Override
    public ShareExperimentVO enterShareExperiment(Long userId, ShareUser shareUser) {
        if (!shareTable.contains(shareUser, userId)) {
            throw new GlobalException("分享不存在");
        }
        if (!shareExperimentMap.containsKey(shareUser)) {
            throw new GlobalException("分享桌面当前不可用");
        }

        ShareExperimentVO shareExperimentVO = shareExperimentMap.get(shareUser);
        shareExperimentVO.setControllable(shareTable.get(shareUser, userId));

        return shareExperimentVO;
    }

    @Override
    public void deleteShareExperiment(Long userId, ShareUser shareUser) {
        // 删除分享表
        shareTable.remove(shareUser, userId);

        // 推送删除分享消息
        ShareDeleteMessage shareDeleteMessage = ShareDeleteMessage.builder()
                .userId(userId)
                .chapterId(shareUser.getChapterId())
                .shareUserId(shareUser.getUserId())
                .build();
        GlobalWebsocket.sendMessage(userId, shareDeleteMessage);

        // 推送关闭分享消息
        ShareCloseMessage shareCloseMessage = ShareCloseMessage.builder()
                .userId(shareUser.getUserId())
                .chapterId(shareUser.getChapterId())
                .build();
        CloseShareWebsocket.sendMessage(shareUser, shareCloseMessage, Sets.newHashSet(userId));
    }

    @Override
    public void deleteAllShareExperiment(Long userId) {
        Map<ShareUser, Boolean> shareMap = shareTable.column(userId);
        if (MapUtils.isNotEmpty(shareMap)) {
            List<ShareUser> shareUsers = new ArrayList<>(shareMap.keySet());
            for (ShareUser shareUser : shareUsers) {
                deleteShareExperiment(userId, shareUser);
            }
        }
    }

    @Override
    public void closeStuToStuShare(Long chapterId) {
        // 查询学生
        List<UserInfoDTO> userInfoDTOS;
        try {
            userInfoDTOS = chapterService.listStudent(chapterId);
        } catch (Exception e) {
            log.warn(e.getMessage());
            return;
        }
        if (CollectionUtils.isEmpty(userInfoDTOS)) {
            return;
        }

        // 用于后续判断用户是否为学生
        Set<Long> userIdSet = userInfoDTOS.stream().map(UserInfoDTO::getId).collect(Collectors.toSet());

        closeSpecificShare(chapterId, userInfoDTOS, userIdSet);
    }

    @Override
    public void closeStuToTeaShare(Long chapterId) {
        // 查询学生
        List<UserInfoDTO> userInfoDTOS;
        try {
            userInfoDTOS = chapterService.listStudent(chapterId);
        } catch (Exception e) {
            log.warn(e.getMessage());
            return;
        }
        if (CollectionUtils.isEmpty(userInfoDTOS)) {
            return;
        }

        // 用于后续判断用户是否为教师
        List<UserInfoDTO> teachers = chapterService.getChapterTeacher(chapterId);
        Set<Long> userIdSet = teachers.stream().map(UserInfoDTO::getId).collect(Collectors.toSet());

        closeSpecificShare(chapterId, userInfoDTOS, userIdSet);
    }

    @Override
    public void cacheShareExperiment(ShareUser shareUser, List<ExperimentContainer> experimentContainers) {
        List<ShareContainerVO> shareContainerVOS = experimentContainers.stream()
                .map(container -> ShareContainerVO.builder()
                        .containerId(container.getContainerId())
                        .containerRole(SLAVE == container.getContainerRole() ?
                                container.getContainerRole().getMessage() + container.getRoleNumber() :
                                container.getContainerRole().getMessage())
                        .desktopUrl(container.getDesktopUrl())
                        .build())
                .collect(Collectors.toList());

        ShareExperimentVO shareExperimentVO = ShareExperimentVO.builder()
                .username(shareUser.getUsername())
                .containers(shareContainerVOS)
                .fullScreen(experimentContainers.get(0).getFullScreen())
                .build();
        shareExperimentMap.put(shareUser, shareExperimentVO);
    }

    @Override
    public void invalidShareExperiment(ShareUser shareUser) {
        shareExperimentMap.remove(shareUser);
    }

    @Override
    public void notifyFullScreen(List<ShareUser> shareUsers, Boolean fullScreen) {
        if (CollectionUtils.isNotEmpty(shareUsers)) {
            for (ShareUser shareUser : shareUsers) {
                // 修改实验表
                ShareExperimentVO shareExperimentVO = shareExperimentMap.get(shareUser);
                if (shareExperimentVO != null) {
                    shareExperimentVO.setFullScreen(fullScreen);
                }

                // 推送全屏信息
                AdjustResolutionMessage adjustResolutionMessage = AdjustResolutionMessage.builder()
                        .userId(shareUser.getUserId())
                        .chapterId(shareUser.getChapterId())
                        .fullScreen(fullScreen)
                        .build();
                CloseShareWebsocket.sendMessage(shareUser, adjustResolutionMessage);
            }
        }
    }

    @Override
    public void switchExperiment(ShareUser shareUser, Integer number) {
        // 修改实验表
        ShareExperimentVO shareExperimentVO = shareExperimentMap.get(shareUser);
        if (shareExperimentVO != null) {
            shareExperimentVO.setNumber(number);
        }

        // 推送切换消息
        SwitchNumberMessage switchNumberMessage = SwitchNumberMessage.builder()
                .userId(shareUser.getUserId())
                .username(shareUser.getUsername())
                .chapterId(shareUser.getChapterId())
                .number(number)
                .build();
        CloseShareWebsocket.sendMessage(shareUser, switchNumberMessage);
    }

    private void closeSpecificShare(Long chapterId, List<UserInfoDTO> userInfoDTOS, Set<Long> userIdSet) {
        for (UserInfoDTO userInfoDTO : userInfoDTOS) {
            ShareUser shareUser = ShareUser.builder()
                    .userId(userInfoDTO.getId())
                    .chapterId(chapterId)
                    .build();
            Map<Long, Boolean> shareMap = shareTable.row(shareUser);

            if (MapUtils.isNotEmpty(shareMap)) {
                List<Long> sharedUserIds = new ArrayList<>(shareMap.keySet()).stream()
                        .filter(userIdSet::contains)
                        .collect(Collectors.toList());
                // 删除分享表
                for (Long sharedUserId : sharedUserIds) {
                    shareTable.remove(shareUser, sharedUserId);
                }
                // 推送关闭分享消息
                ShareCloseMessage shareCloseMessage = ShareCloseMessage.builder()
                        .userId(shareUser.getUserId())
                        .chapterId(shareUser.getChapterId())
                        .build();
                GlobalWebsocket.sendMessage(sharedUserIds, shareCloseMessage);
                CloseShareWebsocket.sendMessage(shareUser, shareCloseMessage, new HashSet<>(sharedUserIds));
            }
        }
    }
}
